#include <8052.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "alarm.h"
#include "board.h"
#include "date.h"
#include "ds1302.h"
#include "lcd.h"
#include "stopwatch.h"

typedef enum{
    CLOCK,
    STOPWATCH,
    SETTING_YEAR,
    SETTING_MONTH,
    SETTING_DATE,
    STOPWATCH_RUNNING,
    COUNTDOWN_SETTING_HR,
    COUNTDOWN_SETTING_MIN,
    COUNTDOWN_SETTING_SEC,
    COUNTDOWN_RUNNING,
    SETTING_HR,
    SETTING_MIN,
    SETTING_SEC,
    SETTING_ALARM_ON_OFF,
    SETTING_ALARM_HR,
    SETTING_ALARM_MIN
} mode_t;
mode_t mode = CLOCK;
mode_t next_mode = CLOCK;

__xdata char lcd_buff1[17] = {0}; // 第一行显示缓冲区
__xdata char lcd_buff2[17] = {0}; // 第二行显示缓冲区

__code const char days_of_week[8][4] = {"SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"};
__code const char alarm_on_off[2][4] = {"OFF", "ON "};

uint8_t year, month, date, hour, minute, second, day; // 日历模式下的时间

uint8_t alarm_on=0; // 是否开启闹铃
uint8_t alarm_hr, alarm_min; // 闹铃时间设定

int main() {
    setp(5<<5, 0x00); // 关蜂鸣器、继电器
    lcd_init(); // 初始化屏幕
    // 初始化定时器
    TH0 = 0xd8; TL0 = 0xf0; // 0.01s
    TMOD = (TMOD&0xf0) | 0x01; // 定时器0方式1
    TR0 = 1; // 开定时器
    ET0 = 1; // 开中断
    EA = 1;
    ds1302_write_enable(); // 允许设定日期时间

    uint8_t buttons_pressed; // 按钮是否按下
    uint8_t buttons_pressed_old=0x00; // 上一次循环中按钮是否按下
    uint8_t buttons_rising_edge; // 是否是这一次循环中刚刚按下
    uint8_t loopcnt=0;
    while (1) {
        // 读取按钮
        buttons_pressed = 0x00;
        buttons_pressed |= P3;
        // 消抖，仅当两次都按下才视作按下
        delay_ms(1);
        buttons_pressed |= P3;
        // 低电平才是按下
        buttons_pressed = ~buttons_pressed;
        buttons_rising_edge = buttons_pressed & (~buttons_pressed_old);
        buttons_pressed_old = buttons_pressed;

        // 拍闹铃机制
        // 当按下任意键的时候，如果闹铃在响
        // 则取消闹铃并忽略这个按键
        if (alarm_flag && (buttons_pressed&0x0f)) {
            alarm_flag = 0;
            buttons_pressed = 0;
            buttons_rising_edge = 0;
        }

        // 清除屏幕缓冲区
        for (int i=0; i<17; ++i) {
            lcd_buff1[i] = 0;
        }
        for (int i=0; i<17; ++i) {
            lcd_buff2[i] = 0;
        }

        // 处理不同模式的逻辑
        switch (mode) {
            case CLOCK:
                // 读取时间
                year = ds1302_from_bcd(ds1302_read_register(DS1302_ADDR_YEAR));
                month = ds1302_from_bcd(ds1302_read_register(DS1302_ADDR_MONTH));
                date = ds1302_from_bcd(ds1302_read_register(DS1302_ADDR_DATE));
                hour = ds1302_from_bcd(ds1302_read_register(DS1302_ADDR_HR));
                minute = ds1302_from_bcd(ds1302_read_register(DS1302_ADDR_MIN));
                second = ds1302_from_bcd(ds1302_read_register(DS1302_ADDR_SEC));
                day = ds1302_from_bcd(ds1302_read_register(DS1302_ADDR_DAY));
                sprintf(lcd_buff1, "20%02d/%02d/%02d %s", year, month, date, days_of_week[day]);
                sprintf(lcd_buff2, "%02d:%02d:%02d", hour, minute, second);
                // 闹铃触发机制
                if (alarm_on && alarm_hr==hour && alarm_min==minute && second==0) {
                    alarm_flag = 1;
                }
                break;
            case STOPWATCH:
            case STOPWATCH_RUNNING:
                strcpy(lcd_buff1, "Stopwatch");
                sprintf(lcd_buff2, "%02d:%02d:%02d.%02d",
                    stopwatch_hr, stopwatch_min, stopwatch_sec, stopwatch_sec_frac);
                break;
            case COUNTDOWN_SETTING_HR:
                strcpy(lcd_buff1, "Countdown");
                if ((loopcnt>>4)&1) {
                    sprintf(lcd_buff2, "%02d:%02d:%02d", stopwatch_hr, stopwatch_min, stopwatch_sec);
                } else {
                    sprintf(lcd_buff2, "  :%02d:%02d", stopwatch_min, stopwatch_sec);
                }
                if (buttons_rising_edge&(1<<2)) {
                    if (stopwatch_hr < 99) {
                        ++stopwatch_hr;
                    }
                } else if (buttons_rising_edge&(1<<3)) {
                    if (stopwatch_hr > 0) {
                        --stopwatch_hr;
                    }
                }
                break;
            case COUNTDOWN_SETTING_MIN:
                strcpy(lcd_buff1, "Countdown");
                if ((loopcnt>>4)&1) {
                    sprintf(lcd_buff2, "%02d:%02d:%02d", stopwatch_hr, stopwatch_min, stopwatch_sec);
                } else {
                    sprintf(lcd_buff2, "%02d:  :%02d", stopwatch_hr, stopwatch_sec);
                }
                if (buttons_rising_edge&(1<<2)) {
                    if (stopwatch_min < 59) {
                        ++stopwatch_min;
                    }
                } else if (buttons_rising_edge&(1<<3)) {
                    if (stopwatch_min > 0) {
                        --stopwatch_min;
                    }
                }
                break;
            case COUNTDOWN_SETTING_SEC:
                strcpy(lcd_buff1, "Countdown");
                if ((loopcnt>>4)&1) {
                    sprintf(lcd_buff2, "%02d:%02d:%02d", stopwatch_hr, stopwatch_min, stopwatch_sec);
                } else {
                    sprintf(lcd_buff2, "%02d:%02d:  ", stopwatch_hr, stopwatch_min);
                }
                if (buttons_rising_edge&(1<<2)) {
                    if (stopwatch_sec < 59) {
                        ++stopwatch_sec;
                    }
                } else if (buttons_rising_edge&(1<<3)) {
                    if (stopwatch_sec > 0) {
                        --stopwatch_sec;
                    }
                }
                break;
            case COUNTDOWN_RUNNING:
                strcpy(lcd_buff1, "Countdown");
                sprintf(lcd_buff2, "%02d:%02d:%02d", stopwatch_hr, stopwatch_min, stopwatch_sec);
                // 计时结束则闹铃响
                if (counting_underflow_flag) {
                    alarm_flag = 1;
                }
                break;
            case SETTING_YEAR:
                if ((loopcnt>>4)&1) {
                    sprintf(lcd_buff1, "20  /%02d/%02d %s", month, date, days_of_week[day]);
                    sprintf(lcd_buff2, "%02d:%02d:%02d", hour, minute, second);
                } else {
                    sprintf(lcd_buff1, "20%02d/%02d/%02d %s", year, month, date, days_of_week[day]);
                    sprintf(lcd_buff2, "%02d:%02d:%02d", hour, minute, second);
                }
                if (buttons_rising_edge&(1<<2)) {
                    if (year < 99) {
                        ++year;
                        day = get_day_of_week_from_date();
                        ds1302_write_register(DS1302_ADDR_YEAR, ds1302_to_bcd(year));
                        ds1302_write_register(DS1302_ADDR_DAY, day);
                    }
                } else if (buttons_rising_edge&(1<<3)) {
                    if (year > 0) {
                        --year;
                        day = get_day_of_week_from_date();
                        ds1302_write_register(DS1302_ADDR_YEAR, ds1302_to_bcd(year));
                        ds1302_write_register(DS1302_ADDR_DAY, day);
                    }
                }
                if (!is_leapyear() && month==2 && date==29) {
                    date = 28;
                    day = get_day_of_week_from_date();
                    ds1302_write_register(DS1302_ADDR_DATE, ds1302_to_bcd(date));
                    ds1302_write_register(DS1302_ADDR_DAY, day);
                }
                break;
            case SETTING_MONTH:
                if ((loopcnt>>4)&1) {
                    sprintf(lcd_buff1, "20%02d/  /%02d %s", year, date, days_of_week[day]);
                    sprintf(lcd_buff2, "%02d:%02d:%02d", hour, minute, second);
                } else {
                    sprintf(lcd_buff1, "20%02d/%02d/%02d %s", year, month, date, days_of_week[day]);
                    sprintf(lcd_buff2, "%02d:%02d:%02d", hour, minute, second);
                }
                if (buttons_rising_edge&(1<<2)) {
                    if (month < 12) {
                        ++month;
                        day = get_day_of_week_from_date();
                        ds1302_write_register(DS1302_ADDR_MONTH, ds1302_to_bcd(month));
                        ds1302_write_register(DS1302_ADDR_DAY, day);
                    }
                } else if (buttons_rising_edge&(1<<3)) {
                    if (month > 1) {
                        --month;
                        day = get_day_of_week_from_date();
                        ds1302_write_register(DS1302_ADDR_MONTH, ds1302_to_bcd(month));
                        ds1302_write_register(DS1302_ADDR_DAY, day);
                    }
                }
                break;
            case SETTING_DATE:
                if ((loopcnt>>4)&1) {
                    sprintf(lcd_buff1, "20%d/%02d/   %s", year, month, days_of_week[day]);
                    sprintf(lcd_buff2, "%02d:%02d:%02d", hour, minute, second);
                } else {
                    sprintf(lcd_buff1, "20%02d/%02d/%02d %s", year, month, date, days_of_week[day]);
                    sprintf(lcd_buff2, "%02d:%02d:%02d", hour, minute, second);
                }
                if (buttons_rising_edge&(1<<2)) {
                    date = next_date();
                    day = get_day_of_week_from_date();
                    ds1302_write_register(DS1302_ADDR_DATE, ds1302_to_bcd(date));
                    ds1302_write_register(DS1302_ADDR_DAY, day);
                } else if (buttons_rising_edge&(1<<3)) {
                    date = prev_date();
                    day = get_day_of_week_from_date();
                    ds1302_write_register(DS1302_ADDR_DATE, ds1302_to_bcd(date));
                    ds1302_write_register(DS1302_ADDR_DAY, day);
                }
                break;
            case SETTING_HR:
                if ((loopcnt>>4)&1) {
                    sprintf(lcd_buff1, "20%02d/%02d/%02d %s", year, month, date, days_of_week[day]);
                    sprintf(lcd_buff2, "%02d:%02d:%02d", hour, minute, second);
                } else {
                    sprintf(lcd_buff1, "20%02d/%02d/%02d %s", year, month, date, days_of_week[day]);
                    sprintf(lcd_buff2, "  :%02d:%02d", minute, second);
                }
                if (buttons_rising_edge&(1<<2)) {
                    if (hour < 23) {
                        ++hour;
                        ds1302_write_register(DS1302_ADDR_HR, ds1302_to_bcd(hour));
                    }
                } else if (buttons_rising_edge&(1<<3)) {
                    if (hour > 0) {
                        --hour;
                        ds1302_write_register(DS1302_ADDR_HR, ds1302_to_bcd(hour));
                    }
                }
                break;
            case SETTING_MIN:
                if ((loopcnt>>4)&1) {
                    sprintf(lcd_buff1, "20%02d/%02d/%02d %s", year, month, date, days_of_week[day]);
                    sprintf(lcd_buff2, "%02d:%02d:%02d", hour, minute, second);
                } else {
                    sprintf(lcd_buff1, "20%02d/%02d/%02d %s", year, month, date, days_of_week[day]);
                    sprintf(lcd_buff2, "%02d:  :%02d", hour, second);
                }
                if (buttons_rising_edge&(1<<2)) {
                    if (minute < 59) {
                        ++minute;
                        ds1302_write_register(DS1302_ADDR_MIN, ds1302_to_bcd(minute));
                    }
                } else if (buttons_rising_edge&(1<<3)) {
                    if (minute > 0) {
                        --minute;
                        ds1302_write_register(DS1302_ADDR_MIN, ds1302_to_bcd(minute));
                    }
                }
                break;
            case SETTING_SEC:
                if ((loopcnt>>4)&1) {
                    sprintf(lcd_buff1, "20%02d/%02d/%02d %s", year, month, date, days_of_week[day]);
                    sprintf(lcd_buff2, "%02d:%02d:  ", hour, minute);
                } else {
                    sprintf(lcd_buff1, "20%02d/%02d/%02d %s", year, month, date, days_of_week[day]);
                    sprintf(lcd_buff2, "%02d:%02d:%02d", hour, minute, second);
                }
                if (buttons_rising_edge&(1<<2)) {
                    if (second < 59) {
                        ++second;
                        ds1302_write_register(DS1302_ADDR_SEC, ds1302_to_bcd(second));
                    }
                } else if (buttons_rising_edge&(1<<3)) {
                    if (second > 0) {
                        --second;
                        ds1302_write_register(DS1302_ADDR_SEC, ds1302_to_bcd(second));
                    }
                }
                break;
            case SETTING_ALARM_ON_OFF:
                if ((loopcnt>>4)&1) {
                    sprintf(lcd_buff1, "Alarm %s", alarm_on_off[alarm_on]);
                    sprintf(lcd_buff2, "%02d:%02d", alarm_hr, alarm_min);
                } else {
                    strcpy(lcd_buff1, "Alarm    ");
                    sprintf(lcd_buff2, "%02d:%02d", alarm_hr, alarm_min);
                }
                if (buttons_rising_edge&(1<<2)) {
                    alarm_on = !alarm_on;
                } else if (buttons_rising_edge&(1<<3)) {
                    alarm_on = !alarm_on;
                }
                break;
            case SETTING_ALARM_HR:
                if ((loopcnt>>4)&1) {
                    sprintf(lcd_buff1, "Alarm %s", alarm_on_off[alarm_on]);
                    sprintf(lcd_buff2, "%02d:%02d", alarm_hr, alarm_min);
                } else {
                    sprintf(lcd_buff1, "Alarm %s", alarm_on_off[alarm_on]);
                    sprintf(lcd_buff2, "  :%02d", alarm_min);
                }
                if (buttons_rising_edge&(1<<2)) {
                    if (alarm_hr < 23) {
                        ++alarm_hr;
                    }
                } else if (buttons_rising_edge&(1<<3)) {
                    if (alarm_hr > 0) {
                        --alarm_hr;
                    }
                }
                break;
            case SETTING_ALARM_MIN:
                if ((loopcnt>>4)&1) {
                    sprintf(lcd_buff1, "Alarm %s", alarm_on_off[alarm_on]);
                    sprintf(lcd_buff2, "%02d:%02d", alarm_hr, alarm_min);
                } else {
                    sprintf(lcd_buff1, "Alarm %s", alarm_on_off[alarm_on]);
                    sprintf(lcd_buff2, "%02d:  ", alarm_hr);
                }
                if (buttons_rising_edge&(1<<2)) {
                    if (alarm_min < 59) {
                        ++alarm_min;
                    }
                } else if (buttons_rising_edge&(1<<3)) {
                    if (alarm_min > 0) {
                        --alarm_min;
                    }
                }
                break;
        }

        // 处理模式切换逻辑
        // 按钮1 切换模式
        // 按钮2 切换设置
        // 按钮3 +
        // 按钮4 -
        switch (mode) {
            case CLOCK:
                if (buttons_rising_edge&1) {
                    next_mode = STOPWATCH;
                } else if(buttons_rising_edge&(1<<1)) {
                    next_mode = SETTING_YEAR;
                }
                break;
            case STOPWATCH:
                if (buttons_rising_edge&1) {
                    next_mode = COUNTDOWN_SETTING_HR;
                } else if(buttons_rising_edge&(1<<1)) {
                    next_mode = STOPWATCH_RUNNING;
                }
                break;
            case SETTING_YEAR:
                if (buttons_rising_edge&(1<<1)) {
                    next_mode = SETTING_MONTH;
                }
                break;
            case SETTING_MONTH:
                if (buttons_rising_edge&(1<<1)) {
                    next_mode = SETTING_DATE;
                }
                break;
            case SETTING_DATE:
                if (buttons_rising_edge&(1<<1)) {
                    next_mode = SETTING_HR;
                }
                break;
            case SETTING_HR:
                if (buttons_rising_edge&(1<<1)) {
                    next_mode = SETTING_MIN;
                }
                break;
            case SETTING_MIN:
                if (buttons_rising_edge&(1<<1)) {
                    next_mode = SETTING_SEC;
                }
                break;
            case SETTING_SEC:
                if (buttons_rising_edge&(1<<1)) {
                    next_mode = CLOCK;
                }
                break;
            case STOPWATCH_RUNNING:
                if (buttons_rising_edge&(1<<1)) {
                    next_mode = STOPWATCH;
                }
                break;
            case COUNTDOWN_SETTING_HR:
                if (buttons_rising_edge&1) {
                    next_mode = SETTING_ALARM_ON_OFF;
                } else if (buttons_rising_edge&(1<<1)) {
                    next_mode = COUNTDOWN_SETTING_MIN;
                }
                break;
            case COUNTDOWN_SETTING_MIN:
                if (buttons_rising_edge&1) {
                    next_mode = CLOCK;
                } else if (buttons_rising_edge&(1<<1)) {
                    next_mode = COUNTDOWN_SETTING_SEC;
                }
                break;
            case COUNTDOWN_SETTING_SEC:
                if (buttons_rising_edge&1) {
                    next_mode = CLOCK;
                } else if (buttons_rising_edge&(1<<1)) {
                    next_mode = COUNTDOWN_RUNNING;
                }
                break;
            case COUNTDOWN_RUNNING:
                if (buttons_rising_edge&(1<<1) || counting_underflow_flag) {
                    // 计时结束也切回设定小时数的界面
                    next_mode = COUNTDOWN_SETTING_HR;
                }
                break;
            case SETTING_ALARM_ON_OFF:
                if (buttons_rising_edge&1) {
                    next_mode = CLOCK;
                } else if (buttons_rising_edge&(1<<1)) {
                    next_mode = SETTING_ALARM_HR;
                }
                break;
            case SETTING_ALARM_HR:
                if (buttons_rising_edge&1) {
                    next_mode = CLOCK;
                } else if (buttons_rising_edge&(1<<1)) {
                    next_mode = SETTING_ALARM_MIN;
                }
                break;
            case SETTING_ALARM_MIN:
                if (buttons_rising_edge&1) {
                    next_mode = CLOCK;
                } else if (buttons_rising_edge&(1<<1)) {
                    next_mode = SETTING_ALARM_ON_OFF;
                }
                break;
        }

        // 刷新LCD显示
        lcd_write_cmd(0x80);
        lcd_write_str(lcd_buff1);
        lcd_write_cmd(0xc0);
        lcd_write_str(lcd_buff2);

        // 切换至新模式的初始化
        if (mode != next_mode) {
            switch (next_mode) {
                case STOPWATCH:
                    counting_flag = 0; // 暂停计时
                    // 如果从秒表之外模式切入则清除计数
                    if (mode != STOPWATCH_RUNNING) {
                        stopwatch_hr = stopwatch_min = stopwatch_sec = stopwatch_sec_frac = 0;
                    }
                    break;
                case STOPWATCH_RUNNING:
                    counting_up = 1; // 设定向上计时
                    stopwatch_hr = stopwatch_min = stopwatch_sec = stopwatch_sec_frac = 0; // 清除计时
                    counting_flag = 1; // 开始计时
                    break;
                case COUNTDOWN_SETTING_HR:
                    // 清除计数
                    if (mode != STOPWATCH_RUNNING) {
                        stopwatch_hr = stopwatch_min = stopwatch_sec = stopwatch_sec_frac = 0;
                    }
                    counting_flag = 0; // 停止计时
                    break;
                case COUNTDOWN_RUNNING:
                    stopwatch_sec_frac = 99; // 确保计满秒数
                    counting_underflow_flag = 0; // 清除溢出标志
                    counting_up = 0; // 向下计时
                    counting_flag = 1; // 开始计时
                    break;
            }
            // 清屏
            lcd_write_cmd(0x01);
            mode = next_mode;
        }

        ++loopcnt;
    }

    return 0;
}

void timer0_isr() __interrupt(1) {
    TH0 = 0xd8; TL0 = 0xf0; // 0.01s
    alarm_loop();
    counting_loop();
}
